/**
 * @file igs_task.c
 * @author Sheng Wen Peng
 * @date 25 Nov 2014
 * @brief IGS task functions
 *    there are three type of tasks which are polling, routine polling, 
 * and routine int. each task will be execute when the delay set to 0.
 *    1. polling task will reduce the delay in the main function.
 *    2. routine polling task will reduce the delay when the igs timer is change, 
 * and will be executed in main function
 *    3. routine int task will reduce the delay in the igs timer interrput,
 * and when delay return to zero, task will be executed. 
 *    
 */

#include "igs_task.h"

/**
 * @brief routine int task variables
 */
#define MAX_NUM_OF_ROUTING_INT_TASK 3 /**< maximum task number can exist in the program */
static igsTask_t *firstRoutineIntTask = NULL; /**< point to the first task */
static igsTask_t routineIntTaskPool[MAX_NUM_OF_ROUTING_INT_TASK]; /**< head of linking list */
static uint32_t routineIntTaskIndex=0; /**< number of task now */

/**
 * @brief routine polling task variables
 */
#define MAX_NUM_OF_ROUTING_POLLING_TASK 20 /**< maximum task number can exist in the program */
static igsTask_t *firstRoutinePollingTask = NULL; /**< point to the first task */
static igsTask_t routinePollingTaskPool[MAX_NUM_OF_ROUTING_POLLING_TASK]; /**< head of linking list */
static uint32_t routinePollingTaskIndex=0; /**< number of task now */

/**
 * @brief polling task variables
 */
#define MAX_NUM_OF_POLLING_TASK 20 /**< maximum task number can exist in the program */
static igsTask_t *firstPollingTask = NULL; /**< point to the first task */
static igsTask_t pollingTaskPool[MAX_NUM_OF_POLLING_TASK]; /**< head of linking list */
static uint32_t pollingTaskIndex=0; /**< number of task now */

/**
 * @brief igs_task_init
 *    igs task init function
 * @param
 * @return
 */
void igs_task_init()
{
	
	firstRoutineIntTask = NULL;
	memset(routineIntTaskPool,0,sizeof(igsTask_t)*MAX_NUM_OF_ROUTING_INT_TASK);
	routineIntTaskIndex=0;//initial timer task
	
  
	firstRoutinePollingTask = NULL;
	memset(routinePollingTaskPool,0,sizeof(igsTask_t)*MAX_NUM_OF_ROUTING_POLLING_TASK);
	routinePollingTaskIndex=0;//initial timer task
	
  firstPollingTask = NULL;
	memset(pollingTaskPool,0,sizeof(igsTask_t)*MAX_NUM_OF_POLLING_TASK);
	pollingTaskIndex=0;//initial timer task
  
}

/**
 * @brief igs_task_add:
 *        add new task into the linking list
 *        now only support period task
 * @param type
 *    task type
 * @param period
 *    period of the new task
 * @param body
 *    function point pointing the new task main function
 * @return task id
 */
uint32_t igs_task_add(enum igsTaskType_t type, uint32_t period, void (*body)(void))
{
	
	uint32_t maxNumOfTask;
	igsTask_t *taskObj_p;
	igsTask_t **taskFirstObj_p;
	igsTask_t *taskPool;
	uint32_t *taskIndex_p;
	
	
	switch(type)
	{
		case IGS_TASK_ROUTINE_INT:
		{
			maxNumOfTask = MAX_NUM_OF_ROUTING_INT_TASK;
			taskObj_p = firstRoutineIntTask;
			taskFirstObj_p = &firstRoutineIntTask;
			taskPool = routineIntTaskPool;
			taskIndex_p = &routineIntTaskIndex;
			break;
		}
		case IGS_TASK_ROUTINE_POLLING:
		{
			maxNumOfTask = MAX_NUM_OF_ROUTING_POLLING_TASK;
			taskObj_p = firstRoutinePollingTask;
			taskFirstObj_p = &firstRoutinePollingTask;
			taskPool = routinePollingTaskPool;
			taskIndex_p = &routinePollingTaskIndex;
			break;
		}
		case IGS_TASK_POLLING:
		{
			maxNumOfTask = MAX_NUM_OF_POLLING_TASK;
			taskObj_p = firstPollingTask;
			taskFirstObj_p = &firstPollingTask;
			taskPool = pollingTaskPool;
			taskIndex_p = &pollingTaskIndex;
			break;
		}
		default:break;
	}
	

	/* when first task is added */
	if (taskObj_p == NULL) 
	{
		// allocate memory from array
		if(*taskIndex_p >= maxNumOfTask) while(1); // memory run out
		taskObj_p = &taskPool[(*taskIndex_p)++];
		
		// initialization
		taskObj_p->delay = period;       
		taskObj_p->period = period;
		taskObj_p->body_function = body;
		taskObj_p->next = NULL;
		*taskFirstObj_p = taskObj_p;
		return (*taskIndex_p)-1;
	}
	
	/* if two or more task is be added */
	//find the tail in the list
	while (taskObj_p->next != NULL) 
	{
		taskObj_p = taskObj_p->next;
	}
	
	// allocate memory from array
	if(*taskIndex_p >= maxNumOfTask) while(1); // memory run out
	taskObj_p->next = &taskPool[(*taskIndex_p)++];
	
	// initialization
	taskObj_p = taskObj_p->next;
	taskObj_p->delay = period;
	taskObj_p->period = period;
	taskObj_p->body_function = body;
	taskObj_p->next = NULL;
	return (*taskIndex_p)-1;
}
/**
 * @breif igs_task_set_task
 *    change period of specify task, period 0 means disable the task
 * @param type 
 *    task type
 * @param id
 *    task id
 * @param period
 *    new period
 */
void igs_task_set_task(enum igsTaskType_t type, uint32_t id, uint32_t period)
{
	igsTask_t *taskPool;
	switch(type)
	{
		case IGS_TASK_ROUTINE_INT:
		{
			taskPool = routineIntTaskPool;
			break;
		}
		case IGS_TASK_ROUTINE_POLLING:
		{
			taskPool = routinePollingTaskPool;
			break;
		}
		case IGS_TASK_POLLING:
		{
			taskPool = pollingTaskPool;
			break;
		}
		default:break;
	}	
	taskPool[id].period = period;
	taskPool[id].delay = period;
}

/**
 * @brief igs_task_main:
 *    external function of task_exe()
 * @param type
 *    type of task
 * @return
 */
void igs_task_main(enum igsTaskType_t type)
{

	igsTask_t *taskObj_p;
	
	switch(type)
	{
		case IGS_TASK_ROUTINE_INT:
		{
			taskObj_p = firstRoutineIntTask;
			break;
		}
		case IGS_TASK_ROUTINE_POLLING:
		{
			taskObj_p = firstRoutinePollingTask;
			break;
		}
		case IGS_TASK_POLLING:
		{
			taskObj_p = firstPollingTask;
			break;
		}
		default:break;
	}
	
	// reduce the delay value in the whole task list
	while (taskObj_p != NULL) {
		if(taskObj_p->period==0)
		{
			//task disable
			taskObj_p = taskObj_p->next;
			continue;
		}
			
		taskObj_p->delay--;
		
		//   if delay value is reduce to 0, 
		// execute the task and set delay value back to its period.
		if (taskObj_p->delay == 0) {
			taskObj_p->delay = taskObj_p->period;
			taskObj_p->body_function();
		}
		taskObj_p = taskObj_p->next;
	}
}
